﻿using System;
using System.Text;
using Unikado.PHPLibs.Tokenizer.Types;

namespace Unikado.PHPLibs.Tokenizer.Highlighter
{

    /// <summary>
    /// Highlighting config of one highlighter type. If you want to highlight Code elements
    /// of a defined <see cref="HighlighterTokenType"/>, you have to define it by a 
    /// instance of this class.
    /// </summary>
    public class HighlighterTypeConfig
    {

        #region - - - - - -   P U B L I C   P R O P E R T I E S   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        /// <summary>
        /// The type (what element will be highlighted with this config setting?
        /// </summary>
        public HighlighterTokenType TokenType { get; internal set; }
        /// <summary>
        /// The forecolor of the Text. (Must be defined in a CSS compatible notation)
        /// </summary>
        public string ForeColor { get; set; }
        /// <summary>
        /// The Background color to use (leave empty for dont use it) (Must be defined in a CSS compatible notation)
        /// </summary>
        public string BackColor { get; set; }
        /// <summary>
        /// Format bold?
        /// </summary>
        public bool Bold { get; set; }
        /// <summary>
        /// If you want to use youre own CSS classname for this config, you can
        /// define it here. If you leave it empty, the Name of the <see cref="TokenType"/>
        /// will be used (1st letter in lower case)
        /// </summary>
        public string CssClassName { get; set; }

        #endregion

        #region - - - - - -   I N T E R N A L   P R O P E R T I E S   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        internal string FinalCssClassName
        {
            get
            {
                if (!string.IsNullOrEmpty(CssClassName)) return CssClassName;
                string name = Enum.GetName(typeof(HighlighterTokenType), TokenType) ?? "Other";
                return char.ToLower(name[0]) + name.Substring(1);
            }
        }

        #endregion

        #region - - - - - -   C O N S T R U C T O R S   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        internal HighlighterTypeConfig(HighlighterTokenType tokenType, string fColor, bool bold=false, string bColor=null)
        {
            TokenType = tokenType;
            ForeColor = fColor;
            Bold = bold;
            BackColor = bColor;
        }

        #endregion

        #region - - - - - -   P U B L I C   M E T H O D S   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        /// <summary>
        /// Return the whole CSS rule, used by this config. (Usable inside a style element or in a CSS file)
        /// </summary>
        /// <returns>string</returns>
        public string ToCssRule()
        {
            var sb = new StringBuilder();
            switch (TokenType)
            {
                case HighlighterTokenType.PhpClass:
                case HighlighterTokenType.PhpFunction:
                case HighlighterTokenType.PhpInterface:
                    sb.Append("\na.").Append(FinalCssClassName).Append(":link, a.");
                    sb.Append(FinalCssClassName).Append(":visited {\n    color: ").Append(ForeColor).Append(';');
                    break;
                default:
                    sb.Append("\n.").Append(FinalCssClassName).Append(" {\n    color: ").Append(ForeColor).Append(';');
                    break;
            }
            if (Bold) sb.Append("\n    font-weight: bold;");
            if (!string.IsNullOrEmpty(BackColor)) sb.Append("\n    background-color: ").Append(BackColor).Append(';');
            sb.Append("\n}");
            return sb.ToString();
        }
        /// <summary>
        /// Writes the token (as HTML span or link element) with the current config, if the token type matches the current config type.
        /// </summary>
        /// <param name="token">The Token</param>
        /// <param name="sb">Here is written if the Method returns true.</param>
        /// <returns>bool</returns>
        public bool WriteHtml(Token token, ref StringBuilder sb)
        {
            switch (TokenType)
            {
                case HighlighterTokenType.Comment:
                    if (token.NormalType != TypeNormal.Comment) return false;
                    break;
                case HighlighterTokenType.DocBlock:
                    if (token.NormalType != TypeNormal.Docblock) return false;
                    break;
                case HighlighterTokenType.Keyword:
                    if (token.RoughType != TypeRough.Keyword &&
                        token.RoughType != TypeRough.KeywordSignature) return false;
                    break;
                case HighlighterTokenType.MagicConstant:
                    if (token.FineType != TypeFine.MagicConstant) return false;
                    break;
                case HighlighterTokenType.NoPhp:
                    if (token.RoughType != TypeRough.None) return false;
                    break;
                case HighlighterTokenType.Number:
                    if (token.RoughType != TypeRough.Number) return false;
                    break;
                case HighlighterTokenType.Operator:
                    if (token.RoughType != TypeRough.Operator) return false;
                    break;
                case HighlighterTokenType.PhpClass:
                    if (token.FineType != TypeFine.PhpClassName) return false;
                    sb.Append("<a href=\"http://php.net/").Append(token.Code.TrimStart('\\'));
                    sb.Append("\" class=\"").Append(FinalCssClassName).Append("\">");
                    sb.Append(token.Code).Append("</a>").Append(token.SpaceAfter);
                    return true;
                case HighlighterTokenType.PhpFunction:
                    if (token.FineType != TypeFine.PhpFunctionName) return false;
                    sb.Append("<a href=\"http://php.net/").Append(token.Code.TrimStart('\\'));
                    sb.Append("\" class=\"").Append(FinalCssClassName).Append("\">");
                    sb.Append(token.Code).Append("</a>").Append(token.SpaceAfter);
                    return true;
                case HighlighterTokenType.PhpInterface:
                    if (token.FineType != TypeFine.PhpInterfaceName) return false;
                    sb.Append("<a href=\"http://php.net/").Append(token.Code.TrimStart('\\'));
                    sb.Append("\" class=\"").Append(FinalCssClassName).Append("\">");
                    sb.Append(token.Code).Append("</a>").Append(token.SpaceAfter);
                    return true;
                case HighlighterTokenType.PhpTag:
                    if (token.RoughType != TypeRough.PhpTag) return false;
                    break;
                case HighlighterTokenType.String:
                    if (token.RoughType != TypeRough.String) return false;
                    break;
                case HighlighterTokenType.SuperGlobal:
                    if (token.FineType != TypeFine.Superglobal) return false;
                    break;
                case HighlighterTokenType.Variable:
                    if (token.NormalType != TypeNormal.Variable &&
                        token.NormalType != TypeNormal.VariablePredefined) return false;
                    break;
                case HighlighterTokenType.Bracket:
                    if (token.RoughType != TypeRough.Bracket) return false;
                    break;
            }
            sb.Append("<span class=\"").Append(FinalCssClassName).Append("\">");
            sb.Append(EscapeForHtml(token.Code)).Append("</span>").Append(token.SpaceAfter);
            return true;
        }

        #endregion

        #region - - - - - -   P R I V A T E   S T A T I C   M E T H O D S   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        /// <summary>
        /// Escapes Characters for use inside HTML.
        /// </summary>
        /// <param name="str">The string to escape</param>
        /// <returns>Escaped string</returns>
        public static string EscapeForHtml(string str)
        {
            return str.Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;");
        }

        #endregion

    }

}